package com.eian.boot.common.core.utils;

import com.eian.boot.common.core.exception.SystemException;
import com.google.common.base.Objects;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.lionsoul.ip2region.xdb.Searcher;
import org.lionsoul.ip2region.xdb.Version;
import org.springframework.core.io.ClassPathResource;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

/**
 * 根据IP查询归属地工具类
 * 只有中国的数据绝大部分精确到了城市，其他国家部分数据只能定位到国家，后前的选项全部是0。
 *
 * @author alex.meng
 * @createTime 2023-2-23 14:26
 */
@Slf4j
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public final class Ip2RegionUtil {
    private static final String DB_RESOURCE_PATH = "ip2region_v4.xdb";
    private static final byte[] vIndex;
    private static final Version version = Version.IPv4;
    private static final Searcher searcher;
    private static final File tempDbFile;

    static {
        try {
            //=== 1. 通过ClassPathResource获取IP数据库文件的输入流
            ClassPathResource resource = new ClassPathResource(DB_RESOURCE_PATH);
            if (!resource.exists()) {
                throw new IOException("IP数据库文件 [" + DB_RESOURCE_PATH + "] 未找到，请确保它位于 src/main/resources/ 目录下");
            }
            //=== 2. 创建临时文件
            Path tempPath = Files.createTempFile("ip2region_", ".xdb");
            tempDbFile = tempPath.toFile();
            tempDbFile.deleteOnExit();
            //=== 3. 将资源内容复制到临时文件
            try (InputStream in = resource.getInputStream();
                 OutputStream out = Files.newOutputStream(tempPath)) {
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = in.read(buffer)) != -1) {
                    out.write(buffer, 0, bytesRead);
                }
            }
            //=== 4. 使用临时文件路径初始化 ip2region
            String dbFilePath = tempDbFile.getAbsolutePath();
            Searcher.verifyFromFile(dbFilePath);
            vIndex = Searcher.loadVectorIndexFromFile(dbFilePath);
            searcher = Searcher.newWithVectorIndex(version, dbFilePath, vIndex);
            log.info("Ip2RegionUtil - 成功加载 IP 数据库，临时路径: {}", dbFilePath);
        } catch (Exception e) {
            log.error("Ip2RegionUtil - 加载IP查询文件失败，请检查文件配置", e);
            throw new SystemException("加载IP查询文件失败", e);
        }
    }

    /**
     * 根据IP查询归属地
     *
     * @param ip IP地址
     * @return region 根据IP查询的归属地，返回格式：国家|区域|省份|城市|ISP
     */
    public static String getRegionByIp(String ip) {
        try {
            return searcher.search(ip);
        } catch (Exception e) {
            log.error("Ip2RegionUtils - 根据IP查询归属地失败: {}", e.getMessage(), e);
            return "未知";
        } finally {
            if (searcher != null) {
                try {
                    searcher.close();
                } catch (Exception e) {
                    log.error("Ip2RegionUtils - 关闭Searcher对象失败: {}", e.getMessage(), e);
                }
            }
        }
    }

    /**
     * 根据IP获取国家
     *
     * @param ip IP地址
     * @return 国家
     */
    public static String getCountryByIp(String ip) {
        return getRegionPartByIp(ip, 0);
    }

    /**
     * 根据IP获取区域
     *
     * @param ip IP地址
     * @return 区域
     */
    public static String getAreaByIp(String ip) {
        return getRegionPartByIp(ip, 1);
    }

    /**
     * 根据IP获取省份
     *
     * @param ip IP地址
     * @return 省份
     */
    public static String getProvinceByIp(String ip) {
        return getRegionPartByIp(ip, 2);
    }

    /**
     * 根据IP获取城市
     *
     * @param ip IP地址
     * @return 城市
     */
    public static String getCityByIp(String ip) {
        return getRegionPartByIp(ip, 3);
    }

    /**
     * 根据IP获取运营商
     *
     * @param ip IP地址
     * @return 运营商
     */
    public static String getIspByIp(String ip) {
        return getRegionPartByIp(ip, 4);
    }

    /**
     * 根据IP获取地址
     *
     * @param ip IP地址
     * @return 地址
     */
    public static String getAddressByIp(String ip) {
        if (Objects.equal(ip, "127.0.0.1") || Objects.equal(ip, "0:0:0:0:0:0:0:1")) {
            return "本机地址";
        }
        return getCountryByIp(ip) + '-' + getProvinceByIp(ip) + '-' + getCityByIp(ip);
    }

    /**
     * 根据IP获取归属地的指定部分
     *
     * @param ip    IP地址
     * @param index 归属地的部分索引，0-国家，1-区域，2-省份，3-城市，4-运营商
     * @return 归属地的指定部分
     */
    private static String getRegionPartByIp(String ip, int index) {
        String region = getRegionByIp(ip);
        String[] parts = region.split("\\|");
        return parts.length > index ? parts[index] : "未知";
    }
}
